THIS_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)

INSTALL_DIR ?= $(THIS_DIR)install
NGINX_SRC ?= $(THIS_DIR)nginx-1.16.1
NGINX_SHA256 ?= f11c2a6dd1d3515736f0324857957db2de98be862461b5a542a3ac6188dbe32b

NGINX_MIRRORS ?= \
    http://nginx.org/download \
    https://packages.gramineproject.io/distfiles

LISTEN_HOST ?= 127.0.0.1
LISTEN_PORT ?= 8002
LISTEN_SSL_PORT ?= 8444

ifeq ($(DEBUG),1)
GRAMINE_LOG_LEVEL = debug
else
GRAMINE_LOG_LEVEL = error
endif

.PHONY: all
all: $(INSTALL_DIR)/sbin/nginx nginx.manifest config testdata ssldata nginx_args
ifeq ($(SGX),1)
all: nginx.manifest.sgx nginx.sig nginx.token
endif

# Note that Gramine doesn't support eventfd() and PR_SET_DUMPABLE, so we manually
# overwrite these macros in the autogenerated configuration header of Nginx.
$(INSTALL_DIR)/sbin/nginx: $(NGINX_SRC)/configure
	cd $(NGINX_SRC) && ./configure --prefix=$(abspath $(INSTALL_DIR)) \
		--without-http_rewrite_module --with-http_ssl_module
	sed -e "s|#define NGX_HAVE_EVENTFD[[:space:]]\+1|#define NGX_HAVE_EVENTFD 0|g" \
		-e "s|#define NGX_HAVE_SYS_EVENTFD_H[[:space:]]\+1|#define NGX_HAVE_SYS_EVENTFD_H 0|g" \
		-e "s|#define NGX_HAVE_PR_SET_DUMPABLE[[:space:]]\+1|#define NGX_HAVE_PR_SET_DUMPABLE 0|g" \
		-i $(NGINX_SRC)/objs/ngx_auto_config.h
	$(MAKE) -C $(NGINX_SRC)
	$(MAKE) -C $(NGINX_SRC) install

$(NGINX_SRC)/configure: $(NGINX_SRC).tar.gz
	tar --touch -xzf $<

$(NGINX_SRC).tar.gz:
	../common_tools/download --output $@ --sha256 $(NGINX_SHA256) \
		$(foreach mirror,$(NGINX_MIRRORS),--url $(mirror)/$(NGINX_SRC).tar.gz)

nginx.manifest: nginx.manifest.template
	gramine-manifest \
		-Dlog_level=$(GRAMINE_LOG_LEVEL) \
		-Darch_libdir=$(ARCH_LIBDIR) \
		-Dinstall_dir=$(INSTALL_DIR) \
		-Dinstall_dir_abspath=$(abspath $(INSTALL_DIR)) \
		$< >$@

# Make on Ubuntu <= 20.04 doesn't support "Rules with Grouped Targets" (`&:`),
# see the helloworld example for details on this workaround.
nginx.manifest.sgx nginx.sig: sgx_sign
	@:

.INTERMEDIATE: sgx_sign
sgx_sign: nginx.manifest $(INSTALL_DIR)/sbin/nginx \
		$(INSTALL_DIR)/conf/nginx-gramine.conf \
		$(TEST_DATA) \
		$(INSTALL_DIR)/conf/server.crt
	gramine-sgx-sign \
		--manifest $< \
		--output $<.sgx

nginx.token: nginx.sig
	gramine-sgx-get-token --output $@ --sig $<

# Nginx configuration and test data
.PHONY: config
config: $(INSTALL_DIR)/conf/nginx-gramine.conf

$(INSTALL_DIR)/conf/nginx-gramine.conf: nginx-gramine.conf.template $(INSTALL_DIR)/sbin/nginx
	sed -e 's|$$(LISTEN_PORT)|'"$(LISTEN_PORT)"'|g' \
		-e 's|$$(LISTEN_SSL_PORT)|'"$(LISTEN_SSL_PORT)"'|g' \
		-e 's|$$(LISTEN_HOST)|'"$(LISTEN_HOST)"'|g' \
	$< > $@

# HTTP docs: Generating random HTML files in $(INSTALL_DIR)/html/random
RANDOM_DIR = $(INSTALL_DIR)/html/random
RANDOM_FILES = \
	$(foreach n,1 2 3 4 5 6 7 8 9 10,2K.$n.html) \
	$(foreach n,1 2 3 4 5,10K.$n.html) \
	$(foreach n,1 2 3 4 5,100K.$n.html) \
	$(foreach n,1 2 3,1M.$n.html) \
	$(foreach n,1 2 3,10M.$n.html) \
	$(foreach n,1 2 3,100.$n.html)

TEST_DATA = $(addprefix $(RANDOM_DIR)/,$(RANDOM_FILES))

# We need to first build and install nginx, otherwise nginx' makefiles think that they already
# filled $(INSTALL_DIR)/html and skip copying installation files.
$(RANDOM_DIR)/%.html: $(INSTALL_DIR)/sbin/nginx
	mkdir -p $(RANDOM_DIR)
	dd if=/dev/urandom of=$@ count=1 bs=$(basename $(basename $(notdir $@))) status=none

.PHONY: testdata
testdata: $(TEST_DATA)

# SSL data: key and x.509 self-signed certificate (to test SSL/TLS)
$(INSTALL_DIR)/conf/server.crt: ssl/ca_config.conf $(INSTALL_DIR)/sbin/nginx
	openssl genrsa -out ssl/ca.key 2048
	openssl req -x509 -new -nodes -key ssl/ca.key -sha256 -days 1024 -out ssl/ca.crt -config ssl/ca_config.conf
	openssl genrsa -out ssl/server.key 2048
	openssl req -new -key ssl/server.key -out ssl/server.csr -config ssl/ca_config.conf
	openssl x509 -req -days 360 -in ssl/server.csr -CA ssl/ca.crt -CAkey ssl/ca.key -CAcreateserial -out ssl/server.crt
	cp -f ssl/* $(INSTALL_DIR)/conf/

.PHONY: ssldata
ssldata: $(INSTALL_DIR)/conf/server.crt

nginx_args:
	gramine-argv-serializer "nginx" "-c" "conf/nginx-gramine.conf" > $@

.PHONY: start-native-server
start-native-server: all
	$(INSTALL_DIR)/sbin/nginx -c conf/nginx-gramine.conf

ifeq ($(SGX),)
GRAMINE = gramine-direct
else
GRAMINE = gramine-sgx
endif

.PHONY: start-gramine-server
start-gramine-server: all
	$(GRAMINE) ./nginx

.PHONY: clean
clean:
	$(RM) *.manifest *.manifest.sgx *.token *.sig OUTPUT result-* tmp nginx_args

.PHONY: distclean
distclean: clean
	$(RM) -r $(NGINX_SRC).tar.gz $(NGINX_SRC) $(INSTALL_DIR)
	$(RM) ssl/server.* ssl/ca.*
